import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Random;
import java.util.TimerTask;
import java.util.Timer;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;


public class GameEngine extends ScreenManager{

	private static final long serialVersionUID = 1L;
	private int dy = -2, dx = 4, score = 0, bonusDx,bonusBallWidth, bonusDy;
	private ArrayList<Obstacle> obstacleList;
	private Random random = new Random();
	private int randomX1, randomX2;
	private int count = 1, checkCollision = 0;
	private boolean isObstacleCollapsed = false, levelSelected = false, isBonusCollapsed = false, isBonusActive = false, isBirdInside = false;
	private String userName;
	private int ballWidth = 0, timer1, timer2, topObstacleLength, minObstacleLength, minObstacleGap, obstacleWidth, levelNo;
	private JLabel bonusLabel ,scoreLabel, lifeLabel;
	private Obstacle obstacle1, obstacle2;
	private Bonus bonus;
	private Bird bird;

	public GameEngine(String name, final int levelNo) {
		this.levelNo = levelNo;
		if(!levelSelected)
			configureLevel();
		bonusDx = dx;
		bonusBallWidth = ballWidth;
		bonusDy = Math.abs(dy);
		
		scoreLabel = new JLabel(String.valueOf(score));
		scoreLabel.setBounds(700,10, 150, 30);
		scoreLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(scoreLabel);
		
		bonusLabel = new JLabel("");
		bonusLabel.setBounds(200,10, 250, 30);
		bonusLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(bonusLabel);
		
		lifeLabel = new JLabel("3");
		lifeLabel.setBounds(50,10, 100, 30);
		lifeLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(lifeLabel);
		
		userName = name;
		bird = new Bird(50,300,ballWidth,ballWidth);
		bonus = new Bonus(-50,-50,30,30);
		randomX1 = random.nextInt(topObstacleLength) + minObstacleLength;
		randomX2 = random.nextInt(600 - randomX1-minObstacleGap- minObstacleLength) + minObstacleLength;
		obstacleList = new ArrayList<Obstacle>();
		Obstacle obstacle1 = new Obstacle(800, 0, obstacleWidth, randomX1);
		Obstacle obstacle2 = new Obstacle(800, 600-randomX2, obstacleWidth, randomX2);
		obstacleList.add(obstacle1);
		obstacleList.add(obstacle2);
		
		final GameObjectPanel goPanel= new GameObjectPanel(bird, obstacleList, bonus, userName);
		setBounds(10, 10, 800, 600);
		getContentPane().add(goPanel);
		

		addKeyListener(new KeyListener() {
			public void keyTyped(KeyEvent e) {

			}

			public void keyPressed(KeyEvent e) {
				displayInfo(e, "KEY PRESSED: ");
			}

			protected void displayInfo(KeyEvent e, String s) {
				int key = e.getKeyCode();
				if(key == KeyEvent.VK_SPACE){
					dy = -1 * dy;
					final Timer t = new Timer();
					t.scheduleAtFixedRate(new TimerTask(){
						public void run(){
							goPanel.getFlappyBird().setPosY(goPanel.getFlappyBird().getPosY() + dy);
							randomX1 = random.nextInt(100*levelNo) + 1;
							updateBonus(goPanel, randomX1);
							updateObstacles();
							addObstacle(5);
							goPanel.setObstacleList(obstacleList);
							checkObstacleCollision(goPanel, t);
							checkBonusCollision(goPanel);
							updateLives(goPanel);
							repaint();

							addKeyListener(new KeyListener() {
								public void keyTyped(KeyEvent e) {

								}

								public void keyPressed(KeyEvent e) {
									displayInfo(e, "KEY PRESSED: ");
								}

								protected void displayInfo(KeyEvent e, String s) {
									int key = e.getKeyCode();
									if(key == KeyEvent.VK_SPACE){
										t.cancel();
									}

								}

								public void keyReleased(KeyEvent arg0) {
									// TODO Auto-generated method stub	
								}

							});

						}

					}, timer1, timer2);
				}
				else if(key == KeyEvent.VK_X){
					dy = -1 * dy;
					final Timer t = new Timer();
					t.scheduleAtFixedRate(new TimerTask(){
						public void run(){
							goPanel.getFlappyBird().setPosY(goPanel.getFlappyBird().getPosY() + dy);
							randomX1 = random.nextInt(100*levelNo) + 1;
							updateBonus(goPanel, randomX1);
							updateObstacles();
							addObstacle(5);
							goPanel.setObstacleList(obstacleList);
							checkObstacleCollision(goPanel, t);
							checkBonusCollision(goPanel);
							updateLives(goPanel);
							saveGame(goPanel);
							repaint();

							addKeyListener(new KeyListener() {
								public void keyTyped(KeyEvent e) {

								}

								public void keyPressed(KeyEvent e) {
									displayInfo(e, "KEY PRESSED: ");
								}

								protected void displayInfo(KeyEvent e, String s) {
									int key = e.getKeyCode();
									if(key == KeyEvent.VK_SPACE){
										t.cancel();
									}

								}

								public void keyReleased(KeyEvent arg0) {
									// TODO Auto-generated method stub	
								}

							});

						}

					}, timer1, timer2);
				}

			}

			public void keyReleased(KeyEvent arg0) {
				// TODO Auto-generated method stub	
			}

		});
	}
	public GameEngine(String name) {
		userName = name;
		obstacleList = new ArrayList<Obstacle>();
		
		loadGame(userName);
		
		bonusDx = dx;
		bonusBallWidth = ballWidth;
		bonusDy = Math.abs(dy);
		
		scoreLabel = new JLabel(String.valueOf(score));
		scoreLabel.setBounds(700,10, 150, 30);
		scoreLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(scoreLabel);
		
		bonusLabel = new JLabel("");
		bonusLabel.setBounds(200,10, 250, 30);
		bonusLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(bonusLabel);
		
		lifeLabel = new JLabel("3");
		lifeLabel.setBounds(50,10, 100, 30);
		lifeLabel.setFont(new Font("Arial", Font.BOLD, 30)); 
		add(lifeLabel);
		
		final GameObjectPanel goPanel= new GameObjectPanel(bird, obstacleList, bonus, userName);
		setBounds(10, 10, 800, 600);
		getContentPane().add(goPanel);

		addKeyListener(new KeyListener() {
			public void keyTyped(KeyEvent e) {

			}

			public void keyPressed(KeyEvent e) {
				displayInfo(e, "KEY PRESSED: ");
			}

			protected void displayInfo(KeyEvent e, String s) {
				int key = e.getKeyCode();
				if(key == KeyEvent.VK_SPACE){
					dy = -1 * dy;
					final Timer t = new Timer();
					t.scheduleAtFixedRate(new TimerTask(){
						public void run(){
							goPanel.getFlappyBird().setPosY(goPanel.getFlappyBird().getPosY() + dy);
							randomX1 = random.nextInt(100*levelNo) + 1;
							updateBonus(goPanel, randomX1);
							updateObstacles();
							addObstacle(5);
							goPanel.setObstacleList(obstacleList);
							checkObstacleCollision(goPanel, t);
							checkBonusCollision(goPanel);
							updateLives(goPanel);
							repaint();

							addKeyListener(new KeyListener() {
								public void keyTyped(KeyEvent e) {

								}

								public void keyPressed(KeyEvent e) {
									displayInfo(e, "KEY PRESSED: ");
								}

								protected void displayInfo(KeyEvent e, String s) {
									int key = e.getKeyCode();
									if(key == KeyEvent.VK_SPACE){
										t.cancel();
									}

								}

								public void keyReleased(KeyEvent arg0) {
									// TODO Auto-generated method stub	
								}

							});

						}

					}, timer1, timer2);
				}
				else if(key == KeyEvent.VK_X){
					dy = -1 * dy;
					final Timer t = new Timer();
					t.scheduleAtFixedRate(new TimerTask(){
						public void run(){
							goPanel.getFlappyBird().setPosY(goPanel.getFlappyBird().getPosY() + dy);
							randomX1 = random.nextInt(100*levelNo) + 1;
							updateBonus(goPanel, randomX1);
							updateObstacles();
							addObstacle(5);
							goPanel.setObstacleList(obstacleList);
							checkObstacleCollision(goPanel, t);
							checkBonusCollision(goPanel);
							updateLives(goPanel);
							saveGame(goPanel);
							repaint();

							addKeyListener(new KeyListener() {
								public void keyTyped(KeyEvent e) {

								}

								public void keyPressed(KeyEvent e) {
									displayInfo(e, "KEY PRESSED: ");
								}

								protected void displayInfo(KeyEvent e, String s) {
									int key = e.getKeyCode();
									if(key == KeyEvent.VK_SPACE){
										t.cancel();
									}

								}

								public void keyReleased(KeyEvent arg0) {
									// TODO Auto-generated method stub	
								}

							});

						}

					}, timer1, timer2);
				}

			}

			public void keyReleased(KeyEvent arg0) {
				// TODO Auto-generated method stub	
			}

		});
	}	
	private void addObstacle(int number){
		if(800 - obstacleList.get(obstacleList.size() - 1).getPosX() >= 200 && count < number){
			randomX1 = random.nextInt(topObstacleLength) + minObstacleLength;
			randomX2 = random.nextInt(600 - randomX1-minObstacleGap- minObstacleLength) + minObstacleLength;
			Obstacle obstacle1 = new Obstacle(800,0, obstacleWidth, randomX1);
			Obstacle obstacle2 = new Obstacle(800, 600-randomX2 , obstacleWidth, randomX2);
			obstacleList.add(obstacle1);
			obstacleList.add(obstacle2);
			count++;
		}
	}
	
	private void updateObstacles(){
		for(int i = 0; i < obstacleList.size()/2; i++){
			if(obstacleList.get(2*i).getPosX() <= -200){
				obstacleList.get(2*i).setPosX(800);
				obstacleList.get(2*i + 1).setPosX(800);
				randomX1 = random.nextInt(topObstacleLength) + minObstacleLength;
				randomX2 = random.nextInt(600 - randomX1-minObstacleGap- minObstacleLength) + minObstacleLength;
				obstacleList.get(2*i + 1).setPosY(600 - randomX2);
				obstacleList.get(2*i).setLength(randomX1);
				obstacleList.get(2*i + 1).setLength(randomX2);
			}
			else{
				obstacleList.get(2*i).setPosX(obstacleList.get(2*i).getPosX() - dx);
				obstacleList.get(2*i + 1).setPosX(obstacleList.get(2*i + 1).getPosX() - dx);
			}
		}
	}
	
	private void updateBonus(GameObjectPanel panel, int number){

		if(isBonusActive){
			bonus.setPosX(bonus.getPosX() - dx);
			if(bonus.getPosX() <= -60 && !isBonusCollapsed)
				isBonusActive = false;
			else if(isBonusCollapsed){
				bonusLabel.setText(String.valueOf(bonus.getType()));
				int i = random.nextInt(500);
				if (i == 1){
					isBonusCollapsed = false;
					isBonusActive = false;
				}
			}
		}
		else{
			if(number < 5 && !isBonusCollapsed){
				bonus.setType(6);
				dx = bonusDx;
				dy = bonusDy*dy/Math.abs(dy);
				ballWidth = bonusBallWidth;
				panel.getFlappyBird().setLength(ballWidth);
				panel.getFlappyBird().setWidth(ballWidth);
				int k = -1;
				for(int i = 0; i < obstacleList.size()/2; i++){
					if(obstacleList.get(i).getPosX() == 800){
						k = i;
					}
				}
				bonus.setPosX(800);
				if(k == -1){
					randomX1 = random.nextInt(400) + minObstacleGap;
				}
				else{
					randomX1 = random.nextInt(600-obstacleList.get(k).getLength()-obstacleList.get(k+1).getLength()) + obstacleList.get(k).getLength() + 15;
				}
				bonus.setPosY(randomX1);
				isBonusActive = true;
				bonusLabel.setText(String.valueOf(bonus.getType()));
			}
		}
	}
	
	private void checkBonusCollision(GameObjectPanel panel){
		if(panel.getFlappyBird().getPosX() - panel.getBonus().getPosX() > -ballWidth + dx && panel.getFlappyBird().getPosX() - panel.getBonus().getPosX() < 30 + dx){
			if(panel.getFlappyBird().getPosY() - panel.getBonus().getPosY() > -ballWidth + dy && panel.getFlappyBird().getPosY() - panel.getBonus().getPosY() < 30){
				panel.getBonus().setPosX(-50);
				panel.getBonus().setPosY(-50);
				handleBonusCollision(panel);
			}
		}
	}
	private void checkObstacleCollision(GameObjectPanel panel, Timer t){
		if((panel.getFlappyBird().getPosY() <= - dx || panel.getFlappyBird().getPosY() >= 600 + 2*dx - 2*ballWidth) && !(bonus.getType() == BonusType.INVINCIBILITY && !isBirdInside)){
			panel.getFlappyBird().decreaseLife();
			isBirdInside = true;

			if(panel.getFlappyBird().getLife() < 0){
				isObstacleCollapsed = true;
				dy = 0;dx=0;
			}
		}
		else{
			if(panel.getObstacleList().get(2*checkCollision).getPosX() - panel.getFlappyBird().getPosX() > obstacleWidth-(2*ballWidth) + dx && panel.getObstacleList().get(2*checkCollision).getPosX() - panel.getFlappyBird().getPosX() < ballWidth - dx && 
			!(bonus.getType() == BonusType.INVINCIBILITY)){
				if(panel.getObstacleList().get(2*checkCollision).getLength() + dy > panel.getFlappyBird().getPosY() && !isBirdInside){
					panel.getFlappyBird().decreaseLife();
					isBirdInside = true;

					if(panel.getFlappyBird().getLife() < 0){
						isObstacleCollapsed = true;
						dy = 0;dx=0;
					}
				}
				else if(panel.getObstacleList().get(2*checkCollision + 1).getPosY() < panel.getFlappyBird().getPosY() + ballWidth - dy && !isBirdInside){
					panel.getFlappyBird().decreaseLife();
					isBirdInside = true;

					if(panel.getFlappyBird().getLife() < 0){
						isObstacleCollapsed = true;
						dy = 0;dx=0;
					}
				}		
			}
			else if(obstacleList.get(2*checkCollision).getPosX() - panel.getFlappyBird().getPosX() <= obstacleWidth-(2*ballWidth) - dx){
				if(checkCollision < (panel.getObstacleList().size() - 2)/2)
					checkCollision++;
				else
					checkCollision = 0;
				isBirdInside = false;
				score++;
				scoreLabel.setText(String.valueOf(score));
			}
		}
		if(isObstacleCollapsed){ 
			t.cancel();
			JOptionPane optionPane = new JOptionPane();
			optionPane.setMessage("Your score is: " + String.valueOf(score));
			optionPane.setMessageType(JOptionPane.OK_OPTION);
			JButton jButton = getButton(optionPane, "OK");
			optionPane.setOptions(new Object[] { jButton });
		    JDialog dialog = optionPane.createDialog(this, "Collapse");
		    dialog.setVisible(true);
		}
	}
	private JButton getButton(final JOptionPane optionPane, String text) {
	    final JButton button = new JButton(text);
	    ActionListener actionListener = new ActionListener() {
	      public void actionPerformed(ActionEvent actionEvent) {
			updateScore();
	    	closeTab();
	    	GameMenu gameMenu = new GameMenu(userName);
	    	gameMenu.setVisible(true);
	      }
	    };
	    button.addActionListener(actionListener);
	    return button;
	  }
	private void closeTab(){
    	this.dispatchEvent(new WindowEvent(this,WindowEvent.WINDOW_CLOSING));
	}
	
	private void updateScore(){
		DatabaseManager.openConnection();
		Statement stmt = null;
		String sql;
		try {
			sql = "insert into flappybirdv2db.highscores(score,userID,levelId) select " + score + ", userID , "+ levelNo +" from flappybirdv2db.users where userName = '" + userName + "'";
			stmt = DatabaseManager.conn.createStatement();
			stmt.executeUpdate(sql);
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(score >= 10 && GameMenu.getLevels() -levelNo == 0){
			System.out.println("1");
			sql = "update flappybirdv2db.userlevels inner join flappybirdv2db.users on userlevels.userId = users.userID set userlevels.levelId = userlevels.levelId + 1  where users.userName = '" + userName + "'";
			try {
				stmt.executeUpdate(sql);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		DatabaseManager.closeConnection();
	}
	private void configureLevel(){
		DatabaseManager.openConnection();
		Statement stmt = null;
		try {
			String sql = "select * from levels where id = " + levelNo;
			stmt = DatabaseManager.conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()){
				timer1 = rs.getInt("timer1");
				timer2 = rs.getInt("timer2");
				topObstacleLength = rs.getInt("topObstacleLength");
				minObstacleLength = rs.getInt("minObstacleLength");
				minObstacleGap = rs.getInt("minObstacleGap");
				obstacleWidth = rs.getInt("obstacleWidth");
				dy = rs.getInt("dy");
				dx = rs.getInt("dx");
				ballWidth = rs.getInt("ballWidth");
			}
			rs.close();
			DatabaseManager.closeConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
 	private void handleBonusCollision(GameObjectPanel panel){
		isBonusCollapsed = true;
		int i = random.nextInt(6);
		bonus.setType(i);
		if(bonus.getType() == BonusType.EXTRA_LIFE)
			panel.getFlappyBird().setLife();
		else if(bonus.getType() == BonusType.SLOW_DOWN){
			dx = dx/2;
			dy = dy/2;
		}
		else if (bonus.getType() == BonusType.BONUS_SCORE)
			score = score + 10;
		else if(bonus.getType() == BonusType.GET_FAT){
			ballWidth = ballWidth*2;
			panel.getFlappyBird().setWidth(ballWidth);
			panel.getFlappyBird().setLength(ballWidth);
		}
		else if(bonus.getType() == BonusType.CAUGHT_ON_FIRE){
			dx = dx+1;
			dy = dy+1;
		}
	}
	private void updateLives(GameObjectPanel panel){

		
		lifeLabel.setText(String.valueOf(panel.getFlappyBird().getLife()));
	}
	private void saveGame(GameObjectPanel panel){
		DatabaseManager.openConnection();
		Statement stmt = null;
		try {
			String obs = "insert into flappybirdv2db.savedgames(count,userName,levelNo,score, ballposX";
			for(int i = 0; i < panel.getObstacleList().size(); i++){
				obs = obs + ",obs"+(i+1)+"Len, obs"+(i+1)+"posX";
			}
			obs = obs + ") values ('"+count+"','"+userName+"','"+levelNo+"','"+score+"','"+panel.getFlappyBird().getPosY()+"'";
			
			for(int i = 0; i < panel.getObstacleList().size(); i++){
				obs = obs + ",'"+panel.getObstacleList().get(i).getLength()+"','"+panel.getObstacleList().get(i).getPosX()+"'";
			}
			obs = obs + ")";
			stmt = DatabaseManager.conn.createStatement();
			stmt.executeUpdate(obs);
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		DatabaseManager.closeConnection();
	};
	private void loadGame(String name){
		
		DatabaseManager.openConnection();
		Statement stmt = null;
		try {	
			String sql = "select * from flappybirdv2db.savedgames where userName='"+name+"' group by userName order by dateadded desc limit 1";
			stmt = DatabaseManager.conn.createStatement();
			ResultSet rs1 = stmt.executeQuery(sql);
			while(rs1.next()){
				levelNo = rs1.getInt("levelNo");
			}
			rs1.close();
			
			sql = "select * from levels where id = " + levelNo;
			stmt = DatabaseManager.conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()){
				timer1 = rs.getInt("timer1");
				timer2 = rs.getInt("timer2");
				topObstacleLength = rs.getInt("topObstacleLength");
				minObstacleLength = rs.getInt("minObstacleLength");
				minObstacleGap = rs.getInt("minObstacleGap");
				obstacleWidth = rs.getInt("obstacleWidth");
				dy = rs.getInt("dy");
				dx = rs.getInt("dx");
				ballWidth = rs.getInt("ballWidth");
			}
			rs.close();
			
			sql = "select * from flappybirdv2db.savedgames where userName='"+name+"' order by dateadded desc limit 1";
			stmt = DatabaseManager.conn.createStatement();
			rs1 = stmt.executeQuery(sql);
			while(rs1.next()){
				int ballposX = rs1.getInt("ballposX");
				bird = new Bird(30, ballposX, ballWidth, ballWidth);
				bonus = new Bonus(-50,-50,30,30);
				levelNo = rs1.getInt("levelNo");
				score = rs1.getInt("score");
				count = rs1.getInt("count");
				
				if(rs1.getInt("obs1Len") != 0){
					obstacle1 = new Obstacle(rs1.getInt("obs1posX"), 0, obstacleWidth, rs1.getInt("obs1Len"));
					obstacle2 = new Obstacle(rs1.getInt("obs2posX"), 600-rs1.getInt("obs2Len"), obstacleWidth, rs1.getInt("obs2Len"));
					obstacleList.add(obstacle1);
					obstacleList.add(obstacle2);
				}
				if(rs1.getInt("obs3Len") != 0){
					obstacle1 = new Obstacle(rs1.getInt("obs3posX"), 0, obstacleWidth, rs1.getInt("obs3Len"));
					obstacle2 = new Obstacle(rs1.getInt("obs4posX"), 600-rs1.getInt("obs4Len"), obstacleWidth, rs1.getInt("obs4Len"));
					obstacleList.add(obstacle1);
					obstacleList.add(obstacle2);
				}
				if(rs1.getInt("obs5Len") != 0){
					obstacle1 = new Obstacle(rs1.getInt("obs5posX"), 0, obstacleWidth, rs1.getInt("obs5Len"));
					obstacle2 = new Obstacle(rs1.getInt("obs6posX"), 600-rs1.getInt("obs6Len"), obstacleWidth, rs1.getInt("obs6Len"));
					obstacleList.add(obstacle1);
					obstacleList.add(obstacle2);
				}
				if(rs1.getInt("obs7Len") != 0){
					obstacle1 = new Obstacle(rs1.getInt("obs7posX"), 0, obstacleWidth, rs1.getInt("obs7Len"));
					obstacle2 = new Obstacle(rs1.getInt("obs8posX"), 600-rs1.getInt("obs8Len"), obstacleWidth, rs1.getInt("obs8Len"));
					obstacleList.add(obstacle1);
					obstacleList.add(obstacle2);
				}
				if(rs1.getInt("obs9Len") != 0){
					obstacle1 = new Obstacle(rs1.getInt("obs9posX"), 0, obstacleWidth, rs1.getInt("obs9Len"));
					obstacle2 = new Obstacle(rs1.getInt("obs10posX"), 600-rs1.getInt("obs10Len"), obstacleWidth, rs1.getInt("obs10Len"));
					obstacleList.add(obstacle1);
					obstacleList.add(obstacle2);
				}
			}
			rs1.close();
			
			DatabaseManager.closeConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

